//	---------------------------------------------------------------------------
//
//	@file		TwSimpleDX11.cpp
//
//	@brief		Example that uses AntTweakBar with DirectX11.
//
//				It draws a Menger sponge, aka Sierpinski cube:
//				http://en.wikipedia.org/wiki/Menger_sponge .
//
//				Cubes shading is augmented with some simple ambient occlusion
//				applied by subdividing each cube face into a 3x3 grid.
//				AntTweakBar is used to add some interacitve controls.
//
//				Note that most of the code here is related to DirectX and
//				Menger sponge generation. AntTweakBar calls are localized
//				in the WinMain function.
//
//				AntTweakBar: http://anttweakbar.sourceforge.net/doc
//				DirectX:	 http://msdn.microsoft.com/directx
//
//	@author		Philippe Decaudin
//
//	---------------------------------------------------------------------------

#include <AntTweakBar.h>
#include <cmath>
#include <vector>
#include <cassert>

#include "d3d10vs2003.h" // workaround to include D3D10.h and D3D11.h with VS2003
#include <d3d11.h>

// Shaders:
// Vertex and pixel shaders are defined in TwSimpleDX11.hlsl and are compiled
// in a pre-build step using the fxc.exe compiler (from the DirectX SDK Aug'09 or later).
// Pre-build commands are:
// fxc /T vs_4_0_level_9_1 /E MainVS /Fh $(IntDir)\TwSimpleDX11_VS.h TwSimpleDX11.hlsl
// fxc /T ps_4_0_level_9_1 /E MainPS /Fh $(IntDir)\TwSimpleDX11_PS.h TwSimpleDX11.hlsl
//
#include "TwSimpleDX11_MainVS.h"
#include "TwSimpleDX11_MainPS.h"


// D3D objects
ID3D11Device *			g_D3DDev = NULL;
ID3D11DeviceContext *	g_D3DDevCtx = NULL;
IDXGISwapChain *		g_SwapChain = NULL;
DXGI_SWAP_CHAIN_DESC	g_SwapChainDesc;
ID3D11RenderTargetView *g_RenderTargetView = NULL;
ID3D11DepthStencilView *g_DepthStencilView = NULL;
D3D11_TEXTURE2D_DESC	g_DepthStencilDesc;
ID3D11VertexShader *	g_VertexShader = NULL;
ID3D11PixelShader *		g_PixelShader = NULL;
ID3D11InputLayout *		g_InputLayout = NULL;
ID3D11Buffer *			g_VertexBuffer = NULL;
ID3D11Buffer *			g_IndexBuffer = NULL;
ID3D11BlendState *		g_BlendState = NULL;
ID3D11DepthStencilState*g_DepthStencilState = NULL;
ID3D11RasterizerState * g_RasterState = NULL;
ID3D11Buffer *			g_ConstantBuffer = NULL;


// Geometry data structures and objects
struct Vector3
{
	float v[3];
	static Vector3 ZERO;
};
Vector3 Vector3::ZERO = { 0, 0, 0 };
struct Matrix4x4
{
	float m[4][4];
	static Matrix4x4 IDENTITY;
};
Matrix4x4 Matrix4x4::IDENTITY = { 1, 0, 0, 0,  0, 1, 0, 0,	0, 0, 1, 0,  0, 0, 0, 1 };
struct Quaternion
{
	float q[4];
	static Quaternion IDENTITY;
};
Quaternion Quaternion::IDENTITY = { 0, 0, 0, 1 };
const float FLOAT_PI = 3.14159265f;
struct Vertex
{
	Vector3 Position;
	Vector3 Normal;
	unsigned int AmbientColor;
};
struct ShaderConstants
{
	Matrix4x4 WorldViewProj;
	Matrix4x4 WorldNorm;
	Vector3 LightDir;
	float LightCoeff;
};
// Each cube face is split into a 3x3 grid
const int CUBE_FACE_VERTEX_COUNT = 4 * 4;		// 16 vertices per face
const int CUBE_FACE_TRIANGLE_COUNT = 2 * 3 * 3; // 18 triangles to be drawn for each face
// Faces color of the sponge wrt to recursion level
const unsigned int COLORS[] = { 0xffffffff, 0xff007fff, 0xff7fff00, 0xffff007f, 0xff0000ff, 0xff00ff00, 0xffff0000 };


// Scene globals
Quaternion g_SpongeRotation;				 // model rotation, set by InitScene
int g_SpongeLevel = 2;						 // number of recursions
bool g_SpongeAO = true;						 // apply ambient occlusion
unsigned int g_SpongeIndicesCount = 0;		 // set by BuildSponge
Vector3 g_LightDir = {-0.5f, -0.2f, 1};		 // light direction vector
float g_CamDistance = 0.7f;					 // camera distance
float g_BackgroundColor[] = {0, 0, 0.5f, 1}; // background color
bool g_Animate = true;						 // enable animation
float g_AnimationSpeed = 0.2f;				 // animation speed


// Some math operators and functions.
// They are defined here to avoid linking with D3DX.
Vector3 operator+(const Vector3& a, const Vector3& b)
{
	Vector3 out;
	out.v[0] = a.v[0] + b.v[0];
	out.v[1] = a.v[1] + b.v[1];
	out.v[2] = a.v[2] + b.v[2];
	return out;
}

Vector3 operator*(float s, const Vector3& a)
{
	Vector3 out;
	out.v[0] = s * a.v[0];
	out.v[1] = s * a.v[1];
	out.v[2] = s * a.v[2];
	return out;
}

float Length(const Vector3& a)
{
	return sqrt(a.v[0]*a.v[0] + a.v[1]*a.v[1] + a.v[2]*a.v[2]);
}

Matrix4x4 Projection(float fov, float aspectRatio, float zNear, float zFar) // Left-handed projection
{
	Matrix4x4 out;
	float yScale = 1.0f / tan(fov / 2.0f);
	float xScale = yScale / aspectRatio;
	out.m[0][0] = xScale;
	out.m[0][1] = out.m[0][2] = out.m[0][3] = 0;
	out.m[1][1] = yScale;
	out.m[1][0] = out.m[1][2] = out.m[1][3] = 0;
	out.m[2][0] = out.m[2][1] = 0;
	out.m[2][2] = zFar / (zFar - zNear);
	out.m[2][3] = 1;
	out.m[3][0] = out.m[3][1] = out.m[3][3] = 0;
	out.m[3][2] = - zNear * zFar / (zFar - zNear);
	return out;
}

Matrix4x4 Translation(const Vector3& t)
{
	Matrix4x4 out(Matrix4x4::IDENTITY);
	out.m[3][0] = t.v[0];
	out.m[3][1] = t.v[1];
	out.m[3][2] = t.v[2];
	return out;
}

Matrix4x4 Scale(float s)
{
	Matrix4x4 out(Matrix4x4::IDENTITY);
	out.m[0][0] = out.m[1][1] = out.m[2][2] = s;
	return out;
}

Matrix4x4 operator*(const Matrix4x4& a, const Matrix4x4& b)
{
	Matrix4x4 out(Matrix4x4::IDENTITY);
	int i, j;
	for (i = 0; i < 4; i++)
		for (j = 0; j < 4; j++)
			out.m[i][j] = a.m[i][0]*b.m[0][j] + a.m[i][1]*b.m[1][j] + a.m[i][2]*b.m[2][j] + a.m[i][3]*b.m[3][j];
	return out;
}

Vector3 operator*(const Vector3& p, const Matrix4x4& a)
{
	Vector3 out;
	float rw = 1.f / (p.v[0]*a.m[0][3] + p.v[1]*a.m[1][3] + p.v[2]*a.m[2][3] + a.m[3][3]);
	out.v[0] = rw  * (p.v[0]*a.m[0][0] + p.v[1]*a.m[1][0] + p.v[2]*a.m[2][0] + a.m[3][0]);
	out.v[1] = rw  * (p.v[0]*a.m[0][1] + p.v[1]*a.m[1][1] + p.v[2]*a.m[2][1] + a.m[3][1]);
	out.v[2] = rw  * (p.v[0]*a.m[0][2] + p.v[1]*a.m[1][2] + p.v[2]*a.m[2][2] + a.m[3][2]);
	return out;
}

Quaternion RotationFromAxisAngle(const Vector3& axis, float angle)
{
	Quaternion out;
	float norm = Length(axis);
	float sina2 = sin(0.5f * angle);
	out.q[0] = sina2 * axis.v[0] / norm;
	out.q[1] = sina2 * axis.v[1] / norm;
	out.q[2] = sina2 * axis.v[2] / norm;
	out.q[3] = cos(0.5f * angle);
	return out;
}

void AxisAngleFromRotation(Vector3& outAxis, float& outAngle, const Quaternion& quat)
{
	float sina2 = sqrt(quat.q[0]*quat.q[0] + quat.q[1]*quat.q[1] + quat.q[2]*quat.q[2]);
	outAngle = 2.0f * atan2(sina2, quat.q[3]);
	float r = (sina2 > 0) ? (1.0f / sina2) : 0;
	outAxis.v[0] = r * quat.q[0];
	outAxis.v[1] = r * quat.q[1];
	outAxis.v[2] = r * quat.q[2];
}

Matrix4x4 QuaternionToMatrix(const Quaternion& quat)
{
	Matrix4x4 out;
	float yy2 = 2.0f * quat.q[1] * quat.q[1];
	float xy2 = 2.0f * quat.q[0] * quat.q[1];
	float xz2 = 2.0f * quat.q[0] * quat.q[2];
	float yz2 = 2.0f * quat.q[1] * quat.q[2];
	float zz2 = 2.0f * quat.q[2] * quat.q[2];
	float wz2 = 2.0f * quat.q[3] * quat.q[2];
	float wy2 = 2.0f * quat.q[3] * quat.q[1];
	float wx2 = 2.0f * quat.q[3] * quat.q[0];
	float xx2 = 2.0f * quat.q[0] * quat.q[0];
	out.m[0][0] = - yy2 - zz2 + 1.0f;
	out.m[0][1] = xy2 + wz2;
	out.m[0][2] = xz2 - wy2;
	out.m[0][3] = 0;
	out.m[1][0] = xy2 - wz2;
	out.m[1][1] = - xx2 - zz2 + 1.0f;
	out.m[1][2] = yz2 + wx2;
	out.m[1][3] = 0;
	out.m[2][0] = xz2 + wy2;
	out.m[2][1] = yz2 - wx2;
	out.m[2][2] = - xx2 - yy2 + 1.0f;
	out.m[2][3] = 0;
	out.m[3][0] = out.m[3][1] = out.m[3][2] = 0;
	out.m[3][3] = 1;
	return out;
}


// Forward declarations
HRESULT InitDevice(HWND wnd);
HRESULT InitScene();
void Cleanup();
LRESULT CALLBACK MessageProc(HWND, UINT, WPARAM, LPARAM);
void Anim();
void Render();
HRESULT BuildSponge(int levelMax, bool aoEnabled);


// Callback function called by AntTweakBar to set the sponge recursion level
void TW_CALL SetSpongeLevelCB(const void *value, void * /*clientData*/)
{
	g_SpongeLevel = *static_cast<const int *>(value);
	BuildSponge(g_SpongeLevel, g_SpongeAO);
}


// Callback function called by AntTweakBar to get the sponge recursion level
void TW_CALL GetSpongeLevelCB(void *value, void * /*clientData*/)
{
	*static_cast<int *>(value) = g_SpongeLevel;
}


// Callback function called by AntTweakBar to enable/disable ambient occlusion
void TW_CALL SetSpongeAOCB(const void *value, void * /*clientData*/)
{
	g_SpongeAO = *static_cast<const bool *>(value);
	BuildSponge(g_SpongeLevel, g_SpongeAO);
}


// Callback function called by AntTweakBar to get ambient occlusion state
void TW_CALL GetSpongeAOCB(void *value, void * /*clientData*/)
{
	*static_cast<bool *>(value) = g_SpongeAO;
}


// Main
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int cmdShow)
{
	// Register our window class
	WNDCLASSEX wcex = { sizeof(WNDCLASSEX), CS_HREDRAW|CS_VREDRAW, MessageProc,
						0L, 0L, instance, NULL, NULL, NULL, NULL, "TwDX11", NULL };
	RegisterClassEx(&wcex);

	// Create a window
	RECT rc = { 0, 0, 640, 480 };
	AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
	HWND wnd = CreateWindow("TwDX11", "AntTweakBar simple example using DirectX11",
							WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
							rc.right-rc.left, rc.bottom-rc.top, NULL, NULL, instance, NULL);
	if (!wnd)
	{
		MessageBox(NULL, "Cannot create window", "Error", MB_OK|MB_ICONERROR);
		return 0;
	}
	ShowWindow(wnd, cmdShow);
	UpdateWindow(wnd);

	// Initialize D3D11
	if (FAILED(InitDevice(wnd)))
	{
		MessageBox(wnd, "Cannot create D3D11 device", "Error", MB_OK|MB_ICONERROR);
		Cleanup();
		return 0;
	}

	// Initialize the 3D scene
	if (FAILED(InitScene()))
	{
		MessageBox(wnd, "Scene initialization failed.", "Error", MB_OK|MB_ICONERROR);
		Cleanup();
		return 0;
	}

	// Initialize AntTweakBar
	if (!TwInit(TW_DIRECT3D11, g_D3DDev))
	{
		MessageBoxA(wnd, TwGetLastError(), "AntTweakBar initialization failed", MB_OK|MB_ICONERROR);
		Cleanup();
		return 0;
	}

	// Create a tweak bar
	TwBar *bar = TwNewBar("TweakBar");
	TwDefine(" GLOBAL help='This example shows how to integrate AntTweakBar into a DirectX11 application.' "); // Message added to the help bar.
	int barSize[2] = {224, 320};
	TwSetParam(bar, NULL, "size", TW_PARAM_INT32, 2, barSize);

	// Add variables to the tweak bar
	TwAddVarCB(bar, "Level", TW_TYPE_INT32, SetSpongeLevelCB, GetSpongeLevelCB, NULL, "min=0 max=3 group=Sponge keyincr=l keydecr=");
	TwAddVarCB(bar, "Ambient Occlusion", TW_TYPE_BOOLCPP, SetSpongeAOCB, GetSpongeAOCB, NULL, "group=Sponge key=o");
	TwAddVarRW(bar, "Rotation", TW_TYPE_QUAT4F, &g_SpongeRotation, "opened=true axisz=-z group=Sponge");
	TwAddVarRW(bar, "Animation", TW_TYPE_BOOLCPP, &g_Animate, "group=Sponge key=a");
	TwAddVarRW(bar, "Animation speed", TW_TYPE_FLOAT, &g_AnimationSpeed, "min=-10 max=10 step=0.1 group=Sponge keyincr=+ keydecr=-");
	TwAddVarRW(bar, "Light direction", TW_TYPE_DIR3F, &g_LightDir, "opened=true axisz=-z showval=false");
	TwAddVarRW(bar, "Camera distance", TW_TYPE_FLOAT, &g_CamDistance, "min=0 max=4 step=0.01 keyincr=PGUP keydecr=PGDOWN");
	TwAddVarRW(bar, "Background", TW_TYPE_COLOR4F, &g_BackgroundColor, "colormode=hls");

	// Main message loop
	MSG msg = {0};
	while (WM_QUIT != msg.message)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			Anim();
			Render();
		}
	}

	TwTerminate();
	Cleanup();

	return (int)msg.wParam;
}


// Create Direct3D device and swap chain
HRESULT InitDevice(HWND wnd)
{
	HRESULT hr = S_OK;

	// Get window size
	RECT rc;
	GetClientRect(wnd, &rc);
	UINT width = rc.right - rc.left;
	UINT height = rc.bottom - rc.top;

	// Create D3D11 device and swap chain
	UINT createDeviceFlags = 0;
	#ifdef _DEBUG
		createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
	#endif
	ZeroMemory(&g_SwapChainDesc, sizeof(g_SwapChainDesc));
	g_SwapChainDesc.BufferCount = 1;
	g_SwapChainDesc.BufferDesc.Width = width;
	g_SwapChainDesc.BufferDesc.Height = height;
	g_SwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	g_SwapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
	g_SwapChainDesc.BufferDesc.RefreshRate.Denominator = 0;
	g_SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	g_SwapChainDesc.OutputWindow = wnd;
	g_SwapChainDesc.SampleDesc.Count = 4;
	g_SwapChainDesc.SampleDesc.Quality = 0;
	g_SwapChainDesc.Windowed = TRUE;
	g_SwapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
	// Try to create a hardware accelerated device with multisample antialiasing first
	hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags,
									   NULL, 0, D3D11_SDK_VERSION, &g_SwapChainDesc, &g_SwapChain,
									   &g_D3DDev, NULL, &g_D3DDevCtx);
	if (FAILED(hr))
	{
		// If failed, try without antialiasing
		g_SwapChainDesc.SampleDesc.Count = 1;
		hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags,
										   NULL, 0, D3D11_SDK_VERSION, &g_SwapChainDesc, &g_SwapChain,
										   &g_D3DDev, NULL, &g_D3DDevCtx);
		if (FAILED(hr))
		{
			// If failed, try to create a reference device
			hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_REFERENCE, NULL, createDeviceFlags,
											   NULL, 0, D3D11_SDK_VERSION, &g_SwapChainDesc, &g_SwapChain,
											   &g_D3DDev, NULL, &g_D3DDevCtx);
			if (SUCCEEDED(hr))
				MessageBox(wnd, "No DX11 hardware acceleration found.\nSwitching to REFERENCE driver (very slow).",
						   "Warning", MB_OK|MB_ICONWARNING);
			else
				return hr;
		}
	}

	// Create a render target and depth-stencil view
	ID3D11Texture2D *backBuffer = NULL, *dsBuffer = NULL;
	hr = g_SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBuffer);
	if (FAILED(hr))
		return hr;

	hr = g_D3DDev->CreateRenderTargetView(backBuffer, NULL, &g_RenderTargetView);
	backBuffer->Release();
	if (FAILED(hr))
		return hr;

	g_DepthStencilDesc.Width = width;
	g_DepthStencilDesc.Height = height;
	g_DepthStencilDesc.MipLevels = 1;
	g_DepthStencilDesc.ArraySize = 1;
	g_DepthStencilDesc.Format = DXGI_FORMAT_D16_UNORM;
	g_DepthStencilDesc.SampleDesc = g_SwapChainDesc.SampleDesc;
	g_DepthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
	g_DepthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	g_DepthStencilDesc.CPUAccessFlags = 0;
	g_DepthStencilDesc.MiscFlags = 0;
	hr = g_D3DDev->CreateTexture2D(&g_DepthStencilDesc, NULL, &dsBuffer);
	if (FAILED(hr))
		return hr;
	hr = g_D3DDev->CreateDepthStencilView(dsBuffer, NULL, &g_DepthStencilView);
	dsBuffer->Release();
	if (FAILED(hr))
		return hr;

	g_D3DDevCtx->OMSetRenderTargets(1, &g_RenderTargetView, g_DepthStencilView);

	// Setup the viewport
	D3D11_VIEWPORT vp;
	vp.Width = (float)width;
	vp.Height = (float)height;
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	g_D3DDevCtx->RSSetViewports(1, &vp);

	return S_OK;
}


// Initialize the 3D objects & shaders
HRESULT InitScene()
{
	HRESULT hr;

	// Define the input layout
	D3D11_INPUT_ELEMENT_DESC layout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Position), D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMA", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Normal), D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(Vertex, AmbientColor), D3D11_INPUT_PER_VERTEX_DATA, 0 }
	};
	hr = g_D3DDev->CreateInputLayout(layout, sizeof(layout)/sizeof(layout[0]), g_MainVS, sizeof(g_MainVS), &g_InputLayout);
	if (FAILED(hr))
		return hr;

	// Set the input layout
	g_D3DDevCtx->IASetInputLayout(g_InputLayout);

	// Create shaders
	hr = g_D3DDev->CreateVertexShader(g_MainVS, sizeof(g_MainVS), NULL, &g_VertexShader);
	if (FAILED(hr))
		return hr;
	hr = g_D3DDev->CreatePixelShader(g_MainPS, sizeof(g_MainPS), NULL, &g_PixelShader);
	if (FAILED(hr))
		return hr;

	// Set shaders
	g_D3DDevCtx->VSSetShader(g_VertexShader, NULL, 0);
	g_D3DDevCtx->PSSetShader(g_PixelShader, NULL, 0);

	// Create vertex and index buffers
	hr = BuildSponge(g_SpongeLevel, g_SpongeAO);
	if (FAILED(hr))
		return hr;

	// Create constant buffer
	D3D11_BUFFER_DESC bd;
	bd.Usage = D3D11_USAGE_DYNAMIC;
	bd.ByteWidth = sizeof(ShaderConstants);
	bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	bd.MiscFlags = 0;
	bd.StructureByteStride = 0;
	hr = g_D3DDev->CreateBuffer(&bd, NULL, &g_ConstantBuffer);
	if (FAILED(hr))
		return hr;

	// Blend state
	D3D11_BLEND_DESC bsd;
	bsd.AlphaToCoverageEnable = FALSE;
	bsd.IndependentBlendEnable = FALSE;
	for (int i = 0; i < 8; i++)
	{
		bsd.RenderTarget[i].BlendEnable = TRUE;
		bsd.RenderTarget[i].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
		bsd.RenderTarget[i].SrcBlend = D3D11_BLEND_SRC_ALPHA;
		bsd.RenderTarget[i].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
		bsd.RenderTarget[i].BlendOp =  D3D11_BLEND_OP_ADD;
		bsd.RenderTarget[i].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
		bsd.RenderTarget[i].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
		bsd.RenderTarget[i].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	}
	g_D3DDev->CreateBlendState(&bsd, &g_BlendState);
	float blendFactors[4] = { 1, 1, 1, 1 };
	g_D3DDevCtx->OMSetBlendState(g_BlendState, blendFactors, 0xffffffff);

	// Depth-stencil state
	D3D11_DEPTH_STENCILOP_DESC od;
	od.StencilFunc = D3D11_COMPARISON_ALWAYS;
	od.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	od.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	od.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
	D3D11_DEPTH_STENCIL_DESC dsd;
	dsd.DepthEnable = TRUE;
	dsd.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	dsd.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
	dsd.StencilEnable = FALSE;
	dsd.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
	dsd.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
	dsd.FrontFace = od;
	dsd.BackFace = od;
	g_D3DDev->CreateDepthStencilState(&dsd, &g_DepthStencilState);
	g_D3DDevCtx->OMSetDepthStencilState(g_DepthStencilState, 0);

	// Rasterizer state
	D3D11_RASTERIZER_DESC rs;
	ZeroMemory(&rs, sizeof(rs));
	rs.FillMode = D3D11_FILL_SOLID;
	rs.CullMode = D3D11_CULL_NONE;
	rs.MultisampleEnable = (g_SwapChainDesc.SampleDesc.Count > 0);
	g_D3DDev->CreateRasterizerState(&rs, &g_RasterState);
	g_D3DDevCtx->RSSetState(g_RasterState);

	// Init model rotation
	Vector3 axis = {-1, 1, 0};
	g_SpongeRotation = RotationFromAxisAngle(axis, FLOAT_PI/4);

	return S_OK;
}


// Clean up D3D objects
void Cleanup()
{
	#define RELEASE_CHECK(p) if (p) { ULONG rc = (p)->Release(); assert(rc == 0); (void)rc; (p) = NULL; }

	if (g_D3DDevCtx)
		g_D3DDevCtx->ClearState();

	RELEASE_CHECK(g_BlendState);
	RELEASE_CHECK(g_DepthStencilState);
	RELEASE_CHECK(g_RasterState);
	RELEASE_CHECK(g_VertexBuffer);
	RELEASE_CHECK(g_IndexBuffer);
	RELEASE_CHECK(g_ConstantBuffer);
	RELEASE_CHECK(g_InputLayout);
	RELEASE_CHECK(g_VertexShader);
	RELEASE_CHECK(g_PixelShader);
	RELEASE_CHECK(g_RenderTargetView);
	RELEASE_CHECK(g_DepthStencilView);
	if (g_SwapChainDesc.Windowed)
		RELEASE_CHECK(g_SwapChain);
	RELEASE_CHECK(g_D3DDevCtx);
	RELEASE_CHECK(g_D3DDev);
}


// Called every time the application receives a message
LRESULT CALLBACK MessageProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	// Send event message to AntTweakBar
	if (TwEventWin(wnd, message, wParam, lParam))
		return 0; // Event has been handled by AntTweakBar

	switch (message)
	{
		case WM_PAINT:
			{
				PAINTSTRUCT ps;
				BeginPaint(wnd, &ps);
				EndPaint(wnd, &ps);
				return 0;
			}
		case WM_SIZE: // Window size has been changed
			if (g_D3DDev) // Resize D3D render target
			{
				// Release render target and depth-stencil view
				ID3D11RenderTargetView *nullRTV = NULL;
				g_D3DDevCtx->OMSetRenderTargets(1, &nullRTV, NULL);
				if (g_RenderTargetView)
				{
					g_RenderTargetView->Release();
					g_RenderTargetView = NULL;
				}
				if (g_DepthStencilView)
				{
					g_DepthStencilView->Release();
					g_DepthStencilView = NULL;
				}

				if (g_SwapChain)
				{
					// Resize swap chain
					g_SwapChainDesc.BufferDesc.Width = LOWORD(lParam);
					g_SwapChainDesc.BufferDesc.Height = HIWORD(lParam);
					g_SwapChain->ResizeBuffers(g_SwapChainDesc.BufferCount, g_SwapChainDesc.BufferDesc.Width,
											   g_SwapChainDesc.BufferDesc.Height, g_SwapChainDesc.BufferDesc.Format,
											   g_SwapChainDesc.Flags);

					// Re-create a render target and depth-stencil view
					ID3D11Texture2D *backBuffer = NULL, *dsBuffer = NULL;
					g_SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBuffer);
					g_D3DDev->CreateRenderTargetView(backBuffer, NULL, &g_RenderTargetView);
					backBuffer->Release();
					g_DepthStencilDesc.Width = g_SwapChainDesc.BufferDesc.Width;
					g_DepthStencilDesc.Height = g_SwapChainDesc.BufferDesc.Height;
					g_D3DDev->CreateTexture2D(&g_DepthStencilDesc, NULL, &dsBuffer);
					g_D3DDev->CreateDepthStencilView(dsBuffer, NULL, &g_DepthStencilView);
					dsBuffer->Release();
					g_D3DDevCtx->OMSetRenderTargets(1, &g_RenderTargetView, g_DepthStencilView);

					// Setup the viewport
					D3D11_VIEWPORT vp;
					vp.Width = (float)g_SwapChainDesc.BufferDesc.Width;
					vp.Height = (float)g_SwapChainDesc.BufferDesc.Height;
					vp.MinDepth = 0.0f;
					vp.MaxDepth = 1.0f;
					vp.TopLeftX = 0;
					vp.TopLeftY = 0;
					g_D3DDevCtx->RSSetViewports(1, &vp);
				}

				// TwWindowSize has been called by TwEventWin, so it is not necessary to call it again here.
			}
			return 0;
		case WM_CHAR:
			if (wParam == VK_ESCAPE)
				PostQuitMessage(0);
			return 0;
		case WM_DESTROY:
			PostQuitMessage(0);
			return 0;
		default:
			return DefWindowProc(wnd, message, wParam, lParam);
	}
}


// Append vertices and indices of a cube to the index and vertex buffers.
// The cube has gradient ambient-occlusion defined per edge.
void AppendCubeToBuffers(std::vector<Vertex>& vertices, std::vector<unsigned int>& indices,
						 const Matrix4x4& xform, float aoRatio, const bool aoEdges[12],
						 const unsigned int faceColors[6])
{
	unsigned int indicesOffset = (unsigned int)vertices.size();

	// Fill vertex buffer

	// Cube faces and edges numbering:
	//		 __________			  _____6____
	//		/		  /|		 /|		   /|
	//	   /	4	 / |<2	   10 5		  9 |
	//	  /_________/  |	   /__|__2___/	7
	//	  |			| 1|	   |  |___4__|__|
	//	3>|    0	|  /	   3  /		 1	/
	//	  |			| /		   | 11		 | 8
	//	  |_________|/		   |/____0___|/
	//		   5^
	// Each face is split in a 3x3 grid, which gives 16 vertices per face and 3x3x2(=18) triangles per face.
	// Ambient occlusion color is set for each of these vertices wrt aoEdges flags.

	const float R = 0.5f; // unit cube radius
	// the 4 corner coordinates for each of the 6 faces
	const Vector3 A[6] = { {-R, -R, -R}, {+R, -R, -R}, {+R, -R, +R}, {-R, -R, +R}, {-R, +R, -R}, {-R, -R, -R} };
	const Vector3 B[6] = { {+R, -R, -R}, {+R, -R, +R}, {-R, -R, +R}, {-R, -R, -R}, {+R, +R, -R}, {+R, -R, -R} };
	const Vector3 C[6] = { {-R, +R, -R}, {+R, +R, -R}, {+R, +R, +R}, {-R, +R, +R}, {-R, +R, +R}, {-R, -R, +R} };
	const Vector3 D[6] = { {+R, +R, -R}, {+R, +R, +R}, {-R, +R, +R}, {-R, +R, -R}, {+R, +R, +R}, {+R, -R, +R} };
	// the 6 face normals
	const Vector3 N[6] = { { 0,  0, -1}, {+1,  0,  0}, { 0,  0, +1}, {-1,  0,  0}, { 0, +1,  0}, { 0, -1,  0} };
	// association between edge indices and the 6 faces
	const int E[6][4] = { {0, 1, 2, 3}, {8, 7, 9, 1}, {4, 5, 6, 7}, {11, 3, 10, 5}, {2, 9, 6, 10}, {0, 8, 4, 11} };

	int face, i, j;
	float u, v;
	bool ao;
	Vertex vertex;
	for (face = 0; face < 6; face++)
		for (j = 0; j < 4; j++)
		{
			v = (j == 1) ? aoRatio : ((j == 2) ? 1.0f - aoRatio : j/3);
			for (i = 0; i < 4; i++)
			{
				u = (i == 1) ? aoRatio : ((i == 2) ? 1.0f - aoRatio : i/3);

				vertex.Position = (1.0f - v) * ((1.0f - u) * A[face] + u * B[face])
								  + v * ((1.0f - u) * C[face] + u * D[face]);
				vertex.Position = vertex.Position * xform;

				vertex.Normal = N[face];

				ao	= (j == 0) && aoEdges[E[face][0]];
				ao |= (i == 3) && aoEdges[E[face][1]];
				ao |= (j == 3) && aoEdges[E[face][2]];
				ao |= (i == 0) && aoEdges[E[face][3]];

				#define DARKEN(r, s) ( (unsigned int)(float(r)*(s)) > 255 ? 255 : (unsigned int)(float(r)*(s)) )
				#define DARKEN_COLOR(c, s) ( 0xff000000 | (DARKEN(((c)>>16)&0xff, s)<<16) | (DARKEN(((c)>>8)&0xff, s)<<8) | DARKEN((c)&0xff, s) )
				vertex.AmbientColor = ao ? DARKEN_COLOR(faceColors[face], 0.75f) : faceColors[face];

				vertices.push_back(vertex);
			}
		}

	// Fill index buffer

	// 3 indices per triangle, 2*3*3 triangles per faces, 6 faces.
	// Vertex index numbering of each face:
	//	  12__13__14___15
	//	   |'. | .'| .'|
	//	   8__'9'_10'__11
	//	   | .'| .'| .'|
	//	   4'__5'__6'__7
	//	   | .'| .'|'. |
	//	   0'__1'__2__'3

	const unsigned short I[CUBE_FACE_TRIANGLE_COUNT][3] =
	{
		{0, 5, 4}, {0, 1, 5},  {1, 6, 5}, {1, 2, 6},  {3, 6, 2}, {3, 7, 6},
		{4, 9, 8}, {4, 5, 9},  {5, 10, 9}, {5, 6, 10},	{6, 11, 10}, {6, 7, 11},
		{8, 9, 12}, {9, 13, 12},  {9, 14, 13}, {9, 10, 14},  {10, 15, 14}, {10, 11, 15}
	};
	int tri;
	for (face = 0; face < 6; face++)
		for (tri = 0; tri < CUBE_FACE_TRIANGLE_COUNT; tri++)
			for (i = 0; i < 3; i++)
				indices.push_back(indicesOffset + I[tri][i] + 16*face); // 16 vertices per face
}


// Recursive function called to fill the vertex and index buffers with the cubes forming the Menger sponge.
void FillSpongeBuffers(int level, int levelMax, std::vector<Vertex>& vertices, std::vector<unsigned int>& indices,
					   const Vector3& center, bool aoEnabled, const bool aoEdges[12], const unsigned int faceColors[6])
{
	float scale = pow(1.0f/3.0f, level);

	if (level == levelMax)
	{
		float aoRatio = pow(3.0f, level) * 0.02f;
		if (aoRatio > 0.4999f)
			aoRatio = 0.4999f;
		Matrix4x4 xform = Scale(scale) * Translation(center);
		AppendCubeToBuffers(vertices, indices, xform, aoRatio, aoEdges, faceColors);
	}
	else
	{
		// Local function which applies AO in one direction
		struct Local
		{
			static void ApplyAO(int i, int j, bool& e0, bool& e1, bool& e2, bool& e3)
			{
				if (i == -1 && j == 0) e0 = e1 = true;
				if (i == +1 && j <= 0) e1 = false;
				if (i == +1 && j >= 0) e0 = false;

				if (i == +1 && j == 0) e2 = e3 = true;
				if (i == -1 && j <= 0) e2 = false;
				if (i == -1 && j >= 0) e3 = false;

				if (j == -1 && i == 0) e1 = e2 = true;
				if (j == +1 && i <= 0) e1 = false;
				if (j == +1 && i >= 0) e2 = false;

				if (j == +1 && i == 0) e0 = e3 = true;
				if (j == -1 && i <= 0) e0 = false;
				if (j == -1 && i >= 0) e3 = false;
			}
		};

		bool aoEdgesCopy[12];
		unsigned int faceColorsCopy[6];
		int i, j, k, l;
		for (i = -1; i <= 1; i++)
			for (j = -1; j <= 1; j++)
				for (k = -1; k <= 1; k++)
					if ( !( (i == 0 && j == 0) || (i == 0 && k == 0) || (j == 0 && k == 0) ) )
					{
						float s = 1.0f/3.0f * scale;
						Vector3 t = { center.v[0] + s * i, center.v[1] + s * j, center.v[2] + s * k };

						for (l = 0; l < 12; l++)
							aoEdgesCopy[l] = aoEdges[l];
						if (aoEnabled)
						{
							Local::ApplyAO( i, j, aoEdgesCopy[8], aoEdgesCopy[9], aoEdgesCopy[10], aoEdgesCopy[11]); // z direction
							Local::ApplyAO( i, k, aoEdgesCopy[1], aoEdgesCopy[7], aoEdgesCopy[5],  aoEdgesCopy[3] ); // y direction
							Local::ApplyAO(-k, j, aoEdgesCopy[0], aoEdgesCopy[2], aoEdgesCopy[6],  aoEdgesCopy[4] ); // x direction
						}

						for (l = 0; l < 6; l++)
							faceColorsCopy[l] = faceColors[l];
						if (k == +1) faceColorsCopy[0] = COLORS[level+1];
						if (i == -1) faceColorsCopy[1] = COLORS[level+1];
						if (k == -1) faceColorsCopy[2] = COLORS[level+1];
						if (i == +1) faceColorsCopy[3] = COLORS[level+1];
						if (j == -1) faceColorsCopy[4] = COLORS[level+1];
						if (j == +1) faceColorsCopy[5] = COLORS[level+1];

						FillSpongeBuffers(level + 1, levelMax, vertices, indices, t, aoEnabled, aoEdgesCopy, faceColorsCopy);
					}
	}
}


// Build sponge vertex and index buffers
HRESULT BuildSponge(int levelMax, bool aoEnabled)
{
	if (g_VertexBuffer)
		g_VertexBuffer->Release();
	if (g_IndexBuffer)
		g_IndexBuffer->Release();
	g_SpongeIndicesCount = 0;

	// Fill vertex and index memory buffers
	static std::vector<Vertex> vertices;
	static std::vector<unsigned int> indices;
	vertices.clear();
	indices.clear();
	bool aoEdges[12] = { false, false, false, false, false, false, false, false, false, false, false, false };
	unsigned int faceColors[6] = { COLORS[0], COLORS[0], COLORS[0], COLORS[0], COLORS[0], COLORS[0] };
	FillSpongeBuffers(0, levelMax, vertices, indices, Vector3::ZERO, aoEnabled, aoEdges, faceColors);

	// Create vertex buffer
	D3D11_BUFFER_DESC bd;
	bd.Usage = D3D11_USAGE_IMMUTABLE;
	bd.ByteWidth = (UINT)vertices.size() * sizeof(Vertex);
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	bd.StructureByteStride = 0;
	D3D11_SUBRESOURCE_DATA initData;
	initData.pSysMem = &vertices[0];
	initData.SysMemPitch = 0;
	initData.SysMemSlicePitch = 0;
	HRESULT hr = g_D3DDev->CreateBuffer(&bd, &initData, &g_VertexBuffer);
	if (FAILED(hr))
		return hr;

	// Create index buffer
	bd.ByteWidth = (UINT)indices.size() * sizeof(unsigned int);
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	initData.pSysMem = &indices[0];
	hr = g_D3DDev->CreateBuffer(&bd, &initData, &g_IndexBuffer);
	if (FAILED(hr))
	{
		g_VertexBuffer->Release();
		return hr;
	}

	g_SpongeIndicesCount = (unsigned int)indices.size();
	return S_OK;
}


// Render the sponge
void DrawSponge()
{
	if (g_SpongeIndicesCount == 0)
		return;

	// Set vertex buffer
	UINT stride = sizeof(Vertex);
	UINT offset = 0;
	g_D3DDevCtx->IASetVertexBuffers(0, 1, &g_VertexBuffer, &stride, &offset);

	// Set index buffer
	g_D3DDevCtx->IASetIndexBuffer(g_IndexBuffer, DXGI_FORMAT_R32_UINT, 0);

	// Set primitive topology
	g_D3DDevCtx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	// Set shaders
	g_D3DDevCtx->VSSetShader(g_VertexShader, NULL, 0);
	g_D3DDevCtx->PSSetShader(g_PixelShader, NULL, 0);

	// Render the primitives
	g_D3DDevCtx->DrawIndexed(g_SpongeIndicesCount, 0, 0);
}


// Copy world/view/proj matrices and light parameters to shader constants
void SetShaderConstants(const Matrix4x4& world, const Matrix4x4& view, const Matrix4x4& proj)
{
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	HRESULT hr = g_D3DDevCtx->Map(g_ConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	if (SUCCEEDED(hr))
	{
		ShaderConstants *cst = (ShaderConstants *)mappedResource.pData;
		cst->WorldViewProj = world * view * proj;
		cst->WorldNorm = world;
		cst->LightDir = (1.0f / Length(g_LightDir)) * g_LightDir;
		cst->LightCoeff = 0.85f;
		g_D3DDevCtx->Unmap(g_ConstantBuffer, 0);
	}
	g_D3DDevCtx->VSSetConstantBuffers(0, 1, &g_ConstantBuffer);
}


// Render a frame
void Render()
{
	// Clear the back buffer
	g_D3DDevCtx->ClearRenderTargetView(g_RenderTargetView, g_BackgroundColor);
	g_D3DDevCtx->ClearDepthStencilView(g_DepthStencilView, D3D11_CLEAR_DEPTH, 1, 0);

	// Set world/view/proj matrices and global shader constants
	float aspectRatio = (float)g_DepthStencilDesc.Width / g_DepthStencilDesc.Height;
	Matrix4x4 proj = Projection(FLOAT_PI/4, aspectRatio, 0.1f, 100.0f);
	float dist = g_CamDistance + 0.4f;
	Vector3 camPosInv = { dist * 0.3f, dist * 0.0f, dist * 2.0f };
	Matrix4x4 view = Translation(camPosInv);
	Matrix4x4 world = QuaternionToMatrix(g_SpongeRotation);
	SetShaderConstants(world, view, proj);

	// Draw the sponge
	DrawSponge();

	// Draw tweak bars
	TwDraw();

	// Present the information rendered in the back buffer to the front buffer (the screen)
	g_SwapChain->Present(0, 0);
}


// Rotating sponge
void Anim()
{
	static DWORD s_PrevTick = GetTickCount();
	DWORD tick = GetTickCount(); // msec
	float dt = float(tick - s_PrevTick) / 1000.0f; // sec
	if (g_Animate && dt > 0 && dt < 0.2f)
	{
		Vector3 axis = Vector3::ZERO;
		float angle = 0;
		AxisAngleFromRotation(axis, angle, g_SpongeRotation);
		if (Length(axis) < 1.0e-6f)
			axis.v[1] = 1;
		angle += g_AnimationSpeed * dt;
		if (angle >= 2.0f*FLOAT_PI)
			angle -= 2.0f*FLOAT_PI;
		else if (angle <= 0)
			angle += 2.0f*FLOAT_PI;
		g_SpongeRotation = RotationFromAxisAngle(axis, angle);
	}
	s_PrevTick = tick;
}
